package org.keyroy.map.render;

import java.awt.Point;
import java.awt.Rectangle;
import java.util.Enumeration;

import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.keyroy.map.core.Map;
import org.keyroy.map.core.overlays.ObjOverlay;
import org.keyroy.map.core.overlays.ObjOverlay.ObjGroup;
import org.keyroy.map.core.sprite.Sprite;

public class SWTMapRenderEngine {
	private static final Color COLOR_GIRD_COLOR = new Color(null, 0, 0, 0);
	private static final Color COLOR_BACKGROUND = new Color(null, 0x66, 0x99, 0xff);
	private static final Color COLOR_BACKGROUND_BORDER = new Color(null, 0x33, 0x66, 0x99);
	private static final Color COLOR_SPRITE_ERROR = new Color(null, 0xcc, 0x00, 0x00);
	private static final Color COLOR_SPRITE_SELECTED = new Color(null, 0xff, 0x69, 0xb4); // 0xff69b4
	public static final Color COLOR_BLOCK = new Color(null, 0xff, 0x66, 0x00);
	public static final Color COLOR_UNBLOCK = new Color(null, 0x00, 0xcc, 0x00);
	public static final Color COLOR_SHADOW = new Color(null, 0, 0, 0);

	public static final Color COLOR_WHITE = new Color(null, 0xff, 0xff, 0xff);

	public static final Color COLOR_POINT_OBJECT = new Color(null, 0xff, 0x7f, 0x50);
	public static final Color COLOR_LINE_OBJECT = new Color(null, 0xff, 0x99, 0x00);
	public static final Color COLOR_AREA_OBJECT = new Color(null, 0xff, 0xa0, 0x7a);
	public static final Color COLOR_LINE = new Color(null, 0xff, 0x99, 0x00);

	private Map map;
	private int mapWidth, mapHeight;

	private ScreenParam screenParam = new ScreenParam();

	public SWTMapRenderEngine() {
	}

	public SWTMapRenderEngine(Map map) {
		setMap(map);
	}

	public void setMap(Map map) {
		this.map = map;
		mapWidth = map.getColumns() * map.getGridWidth();
		mapHeight = map.getRows() * map.getGridHeight();
	}

	public void drawBackgournd(GC gc, Rectangle screen) {
		drawBackgournd(gc, screen, COLOR_BACKGROUND);
	}

	public final void drawBackgournd(GC graphics2d, Rectangle screen, Color color) {
		graphics2d.setBackground(color);
		graphics2d.drawRectangle(0, 0, mapWidth - 1, mapHeight - 1);
	}

	public void drawGrid(GC graphics2d, Rectangle screen) {
		check(screen);
		graphics2d.setForeground(COLOR_GIRD_COLOR);
		graphics2d.setLineWidth(1);
		graphics2d.setLineDash(new int[] { 5, 2 });

		int tx = 0, tw = map.getColumns(), ty = 0, th = map.getRows();
		if (check(screen)) {
			tx = screenParam.tx;
			ty = screenParam.ty;
			tw = screenParam.tw;
			th = screenParam.th;
		}

		for (int i = tx; i < tw; i++) {
			int px = i * map.getGridWidth();
			graphics2d.drawLine(px, 0, px, mapHeight - 1);
		}

		for (int i = ty; i < th; i++) {
			int py = i * map.getGridHeight();
			graphics2d.drawLine(0, py, mapWidth - 1, py);
		}

		graphics2d.drawRectangle(0, 0, mapWidth - 1, mapHeight - 1);
	}

	public void drawPoint(GC graphics2d, Point point) {
		graphics2d.setAlpha(133);

		graphics2d.setLineDash(null);
		graphics2d.setBackground(COLOR_BACKGROUND);
		int tx = point.x * map.getGridWidth();
		int tw = map.getGridWidth();
		int ty = point.y * map.getGridHeight();
		int th = map.getGridHeight();

		graphics2d.fillRectangle(tx, ty, tw, th);
	}

	public void drawArea(GC graphics2d, Rectangle screen, Rectangle area) {
		drawArea(graphics2d, screen, area, COLOR_BACKGROUND);
	}

	public void drawShadow(GC gc) {
		gc.setAlpha(133);
		gc.setBackground(COLOR_SHADOW);
		gc.fillRectangle(0, 0, mapWidth, mapHeight);
	}

	public void drawArea(GC graphics2d, Rectangle screen, Rectangle area, Color color) {
		graphics2d.setAlpha(133);
		if (area.x >= 0 || area.y >= 0) {
			if (screen != null) {
				graphics2d.setClipping(screen.x, screen.y, screen.width, screen.height);
			}

			graphics2d.setLineDash(null);
			graphics2d.setBackground(color);
			graphics2d.setForeground(COLOR_BACKGROUND_BORDER);
			int tx = area.x * map.getGridWidth();
			int tw = (area.width + 1) * map.getGridWidth();
			int ty = area.y * map.getGridHeight();
			int th = (area.height + 1) * map.getGridHeight();

			graphics2d.fillRectangle(tx, ty, tw, th);
			graphics2d.drawRectangle(tx, ty, tw, th);
		}
	}

	public void drawTiles(GC gc, Rectangle screen) {
		drawMapTiles(gc, map.getFloors().tiles, screen);

	}

	public void drawTilesOverlay(GC gc, Rectangle screen) {
		if (map.buildings != null) {
			drawMapTiles(gc, map.buildings.tiles, screen);
		}
	}

	public void drawPointObject(GC gc, ObjOverlay.Point point) {
		gc.setForeground(COLOR_POINT_OBJECT);
		gc.setLineWidth(4);
		gc.setAlpha(200);

		int px = point.x * map.getGridWidth();
		int py = point.y * map.getGridHeight();
		gc.drawRectangle(px, py, map.getGridWidth(), map.getGridHeight());

		if (point.name != null) {
			gc.drawString(point.name, px, py, true);
		}
	}

	public void drawPointObject(GC gc, Rectangle screen) {
		if (map.objOverlay != null) {
			gc.setBackground(COLOR_POINT_OBJECT);
			gc.setAlpha(166);
			Enumeration<ObjGroup> objGroups = map.objOverlay.groups.elements();
			while (objGroups.hasMoreElements()) {
				ObjOverlay.ObjGroup group = (ObjOverlay.ObjGroup) objGroups.nextElement();
				Enumeration<ObjOverlay.Point> points = group.points.elements();
				while (points.hasMoreElements()) {
					ObjOverlay.Point point = (ObjOverlay.Point) points.nextElement();
					int px = mapx2pix(point);
					int py = mapy2pix(point);

					gc.fillRectangle(px, py, map.getGridWidth(), map.getGridHeight());
					gc.drawString(point.name, px, py, true);
				}
			}
		}
	}

	public void drawLineObject(GC gc, Rectangle screen) {
		if (map.objOverlay != null) {
			gc.setBackground(COLOR_LINE_OBJECT);
			gc.setForeground(COLOR_BACKGROUND_BORDER);
			gc.setAlpha(166);

			int gw = map.getGridWidth() >> 1;
			int gh = map.getGridHeight() >> 1;
			int ox = gw >> 1;
			int oy = gh >> 1;

			Enumeration<ObjGroup> objGroups = map.objOverlay.groups.elements();
			// draw rects
			while (objGroups.hasMoreElements()) {
				ObjOverlay.ObjGroup group = (ObjOverlay.ObjGroup) objGroups.nextElement();
				Enumeration<ObjOverlay.Line> lines = group.lines.elements();
				while (lines.hasMoreElements()) {
					ObjOverlay.Line line = (ObjOverlay.Line) lines.nextElement();
					for (int i = 0; i < line.points.size(); i++) {
						ObjOverlay.Point point = line.points.get(i);
						int px = mapx2pix(point);
						int py = mapy2pix(point);

						// gc.fillRectangle(px + ox, py + oy, gw, gh);
						gc.fillArc(px + ox, py + oy, gw, gh, 0, 360);
						gc.drawString(String.valueOf(i), px + ox, py + oy, true);
					}
				}
			}
			gc.setAlpha(255);
			objGroups = map.objOverlay.groups.elements();
			gc.setForeground(COLOR_LINE);
			// draw lines
			while (objGroups.hasMoreElements()) {
				ObjOverlay.ObjGroup group = (ObjOverlay.ObjGroup) objGroups.nextElement();
				Enumeration<ObjOverlay.Line> lines = group.lines.elements();
				while (lines.hasMoreElements()) {
					ObjOverlay.Line line = (ObjOverlay.Line) lines.nextElement();
					for (int i = 0; i < line.points.size() - 1; i++) {
						ObjOverlay.Point point1 = line.points.get(i);
						ObjOverlay.Point point2 = line.points.get(i + 1);

						int sx = mapx2pix(point1) + gw;
						int sy = mapy2pix(point1) + gh;

						int ex = mapx2pix(point2) + gw;
						int ey = mapy2pix(point2) + gh;

						gc.drawLine(sx, sy, ex, ey);
					}
				}
			}
		}
	}

	public void drawAreaObject(GC gc, Rectangle screen) {
		if (map.objOverlay != null) {
			gc.setBackground(COLOR_AREA_OBJECT);
			gc.setForeground(COLOR_BACKGROUND_BORDER);
			gc.setAlpha(255);

			int gw = map.getGridWidth() >> 1;
			int gh = map.getGridHeight() >> 1;

			Enumeration<ObjGroup> objGroups = map.objOverlay.groups.elements();
			// draw rects
			while (objGroups.hasMoreElements()) {
				ObjOverlay.ObjGroup group = (ObjOverlay.ObjGroup) objGroups.nextElement();
				Enumeration<ObjOverlay.Area> areas = group.areas.elements();
				while (areas.hasMoreElements()) {
					ObjOverlay.Area line = (ObjOverlay.Area) areas.nextElement();
					for (int i = 0; i < line.points.size(); i++) {
						ObjOverlay.Point point = line.points.get(i);
						int px = mapx2pix(point);
						int py = mapy2pix(point);

						// gc.fillRectangle(px + ox, py + oy, gw, gh);
						gc.drawString(String.valueOf(i), px + gw, py + gh, false);
					}
				}
			}

			objGroups = map.objOverlay.groups.elements();

			gc.setAlpha(166);
			// draw rects
			while (objGroups.hasMoreElements()) {
				ObjOverlay.ObjGroup group = (ObjOverlay.ObjGroup) objGroups.nextElement();
				Enumeration<ObjOverlay.Area> lines = group.areas.elements();
				while (lines.hasMoreElements()) {
					ObjOverlay.Area area = (ObjOverlay.Area) lines.nextElement();
					if (area.buf == null) {
						area.buf = new int[area.points.size() * 2];
						for (int i = 0; i < area.buf.length; i += 2) {
							ObjOverlay.Point point = area.points.get(i / 2);
							int px = mapx2pix(point);
							int py = mapy2pix(point);
							area.buf[i] = px + gw;
							area.buf[i + 1] = py + gh;
						}
					}
					gc.fillPolygon(area.buf);
				}
			}
		}
	}

	private final int mapx2pix(ObjOverlay.Point point) {
		return mapx2pix(point.x);
	}

	private final int mapy2pix(ObjOverlay.Point point) {
		return mapy2pix(point.y);
	}

	private final int mapx2pix(int mapx) {
		return mapx * map.getGridWidth();
	}

	private final int mapy2pix(int mapy) {
		return mapy * map.getGridHeight();
	}

	public void drawMapTiles(GC gc, Sprite[][] tiles, Rectangle screen) {
		if (tiles == null) {
			return;
		}
		if (screen != null) {
			gc.setClipping(screen.x, screen.y, screen.width, screen.height);
		}

		int tx = 0, tw = map.getColumns(), ty = 0, th = map.getRows();
		if (check(screen)) {
			tx = screenParam.tx;
			ty = screenParam.ty;
			tw = screenParam.tw;
			th = screenParam.th;
		}
		gc.setAlpha(255);
		for (int i = tx; i < tw; i++) {
			for (int j = ty; j < th; j++) {
				if (tiles[i][j] != null) {
					int px = i * map.getGridWidth();
					int py = j * map.getGridHeight();

					if (tiles[i][j].ID == Sprite.SPRITE_DELETE_ID) {
						(gc).fillRectangle(px, py, map.getGridWidth(), map.getGridHeight());
					} else {
						{ // swt
							Image image = SWTResourceLoader.getSWTImage(tiles[i][j]);
							if (image != null) {
								(gc).drawImage(image, px, py);
							} else {
								gc.setBackground(COLOR_SPRITE_ERROR);
								(gc).fillRectangle(px, py, map.getGridWidth(), map.getGridHeight());
							}
						}
					}

				}
			}
		}
	}

	public void drawBlockTiles(GC gc, Sprite[][] tiles, Rectangle screen) {
		if (tiles == null) {
			return;
		}

		int tx = 0, tw = map.getColumns(), ty = 0, th = map.getRows();
		if (check(screen)) {
			tx = screenParam.tx;
			ty = screenParam.ty;
			tw = screenParam.tw;
			th = screenParam.th;
		}
		gc.setAlpha(133);

		gc.setBackground(COLOR_BLOCK);
		gc.setForeground(COLOR_BACKGROUND_BORDER);

		for (int i = tx; i < tw; i++) {
			for (int j = ty; j < th; j++) {
				if (tiles[i][j] != null && tiles[i][j].ID != Sprite.SPRITE_DELETE_ID) {
					if (tiles[i][j].isBlock) {
						int px = i * map.getGridWidth();
						int py = j * map.getGridHeight();
						gc.fillRectangle(px, py, map.getGridWidth(), map.getGridHeight());
						gc.drawRectangle(px, py, map.getGridWidth(), map.getGridHeight());
					}
				}
			}
		}
	}

	public final void drawAllSprites(GC gc, Rectangle screen, Sprite sprite, Sprite[][] tiles) {

		gc.setLineDash(new int[] { 5, 2 });
		
		gc.setBackground(COLOR_SPRITE_SELECTED);
		gc.setForeground(COLOR_BACKGROUND_BORDER);
		gc.setAlpha(200);

		int tx = 0, tw = map.getColumns(), ty = 0, th = map.getRows();
		if (check(screen)) {
			tx = screenParam.tx;
			ty = screenParam.ty;
			tw = screenParam.tw;
			th = screenParam.th;
		}
		for (int i = tx; i < tw; i++) {
			for (int j = ty; j < th; j++) {
				if (tiles[i][j] != null && tiles[i][j].ID == sprite.ID) {
					int px = i * map.getGridWidth();
					int py = j * map.getGridHeight();

					gc.fillRectangle(px, py, map.getGridWidth(), map.getGridHeight());
					gc.drawRectangle(px, py, map.getGridWidth(), map.getGridHeight());
				}
			}
		}
	}

	public void drawSprites(GC graphics2d, Sprite[][] sprites, Point point) {
		if (sprites != null) {
			graphics2d.setBackground(COLOR_SPRITE_ERROR);
			graphics2d.setAlpha(255);
			for (int x = 0; x < sprites.length; x++) {
				for (int y = 0; y < sprites[x].length; y++) {
					Sprite sprite = sprites[x][y];
					if (sprite != null) {
						int tx = (x + point.x) * map.getGridWidth();
						int ty = (y + point.y) * map.getGridHeight();

						{ // swt
							Image image = SWTResourceLoader.getSWTImage(sprite);
							if (image == null) {
								(graphics2d).fillRectangle(tx, ty, map.getGridWidth(), map.getGridHeight());
							} else {
								(graphics2d).drawImage(image, tx, ty);
							}
						}
					}
				}
			}
			graphics2d.setAlpha(133);
			graphics2d.setBackground(COLOR_WHITE);
			graphics2d.drawRectangle(point.x * map.getGridWidth(), point.y * map.getGridHeight(),
					sprites.length * map.getGridWidth(), sprites[0].length * map.getGridHeight());

		}
	}

	public Point findPointInMap(int px, int py) {
		return new Point(px / map.getGridWidth(), py / map.getGridHeight());
	}

	private boolean check(Rectangle screen) {
		if (screen != null) {
			if (screenParam.x != screen.x || screenParam.y != screen.y || screenParam.width != screen.width
					|| screenParam.height != screen.height) {
				screenParam.x = screen.x;
				screenParam.y = screen.y;
				screenParam.width = screen.width;
				screenParam.height = screen.height;

				screenParam.tx = screen.x / map.getGridWidth();
				screenParam.ty = screen.y / map.getGridHeight();

				screenParam.tw = (screen.x + screen.width) / map.getGridWidth() + screenParam.tx;
				screenParam.th = (screen.y + screen.height) / map.getGridHeight() + screenParam.ty;

				return true;
			}
		}

		return false;
	}

	private class ScreenParam {
		private int x, y, width, height;
		private int tx, ty, tw, th;
	}

}
